﻿#include "ThreadCache.h"
#include "CentralCache.h"

void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.PopRange(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpan(start, size);
}


void* ThreadCache::Allocate(size_t size)
{
	/*我们大致的思路是：ThreadCache中将一系列同尺寸的内存块按照链表的形式连接起来，共同构成哈希桶的映射结构，并且提供需要尺寸的内存块，如果需要尺寸的内存块用完了，就向Central进行申请。但是如果以1个字节进行划分哈希桶的话，那么256KB就有256*1024的哈希桶，消耗太大。所以我们需要以一定的规则设计哈希桶。
	 */

	 // 我们需要设计对齐和映射规则(控制threadcache尺寸小于256K)
	 // [1,128]						8byte对齐			freelist[0,16)
	 // [128+1,1024]				16byte对齐			freelist[16,72)
	 // [1024+1,8*1024]				128byte对齐			freelist[72,128)
	 // [8*1024+1,64*1024] `		1024byte对齐			freelist[128,184)
	 // [64*1024+1,256*1024]		8*1024byte对齐		freelist[184,208)

	assert(size <= MAX_SIZE);

	// 获得当前应该申请内存大小
	size_t alginSize = SizeClass::RoundUp(size);
	// 获得对应哈希桶的下标
	size_t index = SizeClass::GetBucketIndex(size);
	// 获取内存
	void* obj = nullptr;

	// 当前哈希桶不为空时
	if (!_freeLists[index].Empty())
	{
		obj = _freeLists[index].Pop();
	}
	else // 当前哈希桶为空
	{
		obj = FetchFromCentralCache(index, alginSize);
	}

	return obj;
}

void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_SIZE);

	// 对应哈希桶的自由链表直接头插即可
	size_t index = SizeClass::GetBucketIndex(size);
	_freeLists[index].Push(ptr);

	// 插入之后进行合并判断
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}

}

void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	assert(size);

	// 慢开始反馈调节算法
	// 1、最开始不会一次向central cache一次批量要太多，因为要太多了可能用不完
	// 2、如果你不要这个size大小内存需求，那么batchNum就会不断增长，直到上限
	// 3、size越大，一次向central cache要的batchNum就越小
	// 4、size越小，一次向central cache要的batchNum就越大

	// windows.h头文件中也有一个min的宏函数，因为模板的实例化在宏替换的后面，所以优先匹配的宏函数
	// size_t batchNum = std::min(_freeList[index].MaxSize(), SizeClass::NumMoveSize(size));
	// 直接使用windows.h中的宏函数即可
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));

	if (_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;
	}

	// 输出型参数，用来获得用_freeList链接的一批小块内存
	void* start = nullptr;
	void* end = nullptr;
	// 因为当前的span不一定能够满足batchNum的需求，所以这里会返回一个实际的切割数据
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);

	// 断言一下，方便debug
	assert(actualNum > 0);
	// 得到了一批小内存块
	if (actualNum == 1)
	{
		assert(start == end);
	}
	else
	{
		// 先将分配的一块内存取出，在将剩下的一小批内存块链表插入_freeLists
		_freeLists[index].PushRange(NextObj(start), end, actualNum - 1);
	}

	return start;
}